package com.stars.easyms.schedule.handler.mvchandler;

import com.stars.easyms.schedule.bean.DataMsg;
import com.stars.easyms.schedule.core.DistributedScheduleManager;
import com.stars.easyms.schedule.bean.DbScheduleConfig;
import com.stars.easyms.schedule.bean.DbScheduleUserInfo;
import com.stars.easyms.schedule.enums.LoginStatus;
import com.stars.easyms.schedule.enums.YesOrNo;
import com.stars.easyms.schedule.handler.DbScheduleMvcHandler;
import com.stars.easyms.schedule.service.DistributedScheduleService;
import com.stars.easyms.schedule.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.*;
import java.sql.Timestamp;

/**
 * 基础MvcHandler类
 *
 * @author guoguifang
 */
abstract class BaseDbScheduleMvcHandler implements DbScheduleMvcHandler {

    protected final static Logger logger = LoggerFactory.getLogger(BaseDbScheduleMvcHandler.class);

    protected boolean isOpenLogin() {
        DbScheduleConfig dbScheduleConfig = DistributedScheduleManager.getSingleInstance().getDbScheduleConfig();
        return dbScheduleConfig != null && YesOrNo.YES == YesOrNo.forCode(dbScheduleConfig.getOpenLogin());
    }

    protected boolean isUseSso() {
        return YesOrNo.YES == YesOrNo.forCode(DistributedScheduleManager.getSingleInstance().getDbScheduleConfig().getUseSso());
    }

    /**
     * 检查权限，如果登录状态为成功则更新登录时间为最新时间
     * @param request 请求对象
     * @return 登录状态
     */
    @Override
    public LoginStatus checkAndRefreshLoginStatus(HttpServletRequest request) {
        LoginStatus loginStatus = getLoginStatus(request);
        if (LoginStatus.SUCCESS == loginStatus) {
            String userName = getHeaderValue(request, "user-name");
            String loginToken = getHeaderValue(request, "login-token");
            updateLoginTime(userName, loginToken, null);
        }
        return loginStatus;
    }

    protected int updateLoginTime(String userName, String loginToken, Long version) {
        DbScheduleUserInfo updateUserInfo = new DbScheduleUserInfo();
        updateUserInfo.setUserName(userName);
        updateUserInfo.setVersion(version);
        updateUserInfo.setLoginToken(loginToken);
        updateUserInfo.setLoginTime(new Timestamp(System.currentTimeMillis()));
        return DistributedScheduleService.getInstance().updateDbScheduleUserInfo(updateUserInfo);
    }

    /**
     * 获取当前用户登录状态
     * @param request 请求
     * @return 登录状态
     */
    protected LoginStatus getLoginStatus(HttpServletRequest request) {
         if (!isOpenLogin()) {
            return LoginStatus.SUCCESS;
        }
        // 若在request的header里获取不到userName和loginToken，则表示未登录
        String userName = getHeaderValue(request, "user-name");
        String loginToken = getHeaderValue(request, "login-token");
        if (userName == null || loginToken == null) {
            return LoginStatus.FAIL;
        }
        if ("null".equalsIgnoreCase(userName) || "null".equalsIgnoreCase(loginToken)) {
            return LoginStatus.SESSION_INVALID;
        }
        DbScheduleUserInfo dbScheduleUserInfo = new DbScheduleUserInfo();
        dbScheduleUserInfo.setUserName(userName);
        dbScheduleUserInfo.setLoginToken(loginToken);
        dbScheduleUserInfo = DistributedScheduleService.getInstance().getDbScheduleUserInfo(dbScheduleUserInfo);
        // 如果根据loginToken获取不到数据则表示loginToken已变动，若开启了单点登录则表示该用户已在其他地方登录，若没开启单点登录则为登录失败
        if (dbScheduleUserInfo == null) {
            if (isUseSso()) {
                return LoginStatus.SSO_EFFECT;
            }
            return LoginStatus.FAIL;
        }
        // 判断当前用户会话是否超时
        DbScheduleConfig dbScheduleConfig = DistributedScheduleManager.getSingleInstance().getDbScheduleConfig();
        if (dbScheduleUserInfo.getCurrentTime().getTime() - dbScheduleUserInfo.getLoginTime().getTime() > dbScheduleConfig.getSessionTimeoutMilli()) {
            return LoginStatus.SESSION_TIMEOUT;
        }
        return LoginStatus.SUCCESS;
    }

    protected String getHeaderValue(HttpServletRequest request, String name) {
        return getHeaderValue(request, name, null);
    }

    protected String getHeaderValue(HttpServletRequest request, String name, String defaultValue) {
        String value = request.getHeader(name);
        if (StringUtils.isBlank(value)) {
            return defaultValue;
        }
        return value;
    }

    protected String getParameterValue(HttpServletRequest request, String parameter) {
        return getParameterValue(request, parameter, null);
    }

    protected String getParameterValue(HttpServletRequest request, String parameter, String defaultValue) {
        String value = request.getParameter(parameter);
        if (StringUtils.isBlank(value)) {
            return defaultValue;
        }
        return value;
    }

    @ExceptionHandler(value = Throwable.class)
    @ResponseBody
    public DataMsg exceptionHandler(HttpServletRequest request, Throwable throwable) {
        DataMsg dataMsg = DataMsg.getFailDataMsg();
        dataMsg.setMsgCode("systemException");
        logger.error("System exception: " + request.getRequestURL() + "!", throwable);
        return dataMsg;
    }

    @Override
    public boolean equals(Object o) {
        return o.getClass() == this.getClass();
    }

    @Override
    public int hashCode() {
        return 1;
    }

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @RequestMapping(method = RequestMethod.GET)
    protected @interface DbScheduleGet {

        @AliasFor(annotation = RequestMapping.class) String[] value() default {};

    }

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    protected @interface DbSchedulePost {

        @AliasFor(annotation = RequestMapping.class) String[] value() default {};

    }
}
